home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-01-04 | 15.4 KB | 395 lines | [TEXT/R*ch] |
- Writing BBEdit Externals
-
-
- INTRODUCTION
-
- BBEdit version 2.2 has a facility for calling code modules which are not
- part of the application itself. The main reason for this facility is so
- that users and third-party programmers can add specific functionality
- to BBEdit which goes beyond BBEdit's own charter. For example, one such
- code module might prepend Usenet attributions to each line in a selected
- range of text. This is a useful function, but it's not of interest to
- everyone.
-
- GENERAL GUIDELINES
-
- BBEdit code externals are built as standalone code resources. The
- capability to build such resources is an integral part of THINK C
- and THINK Pascal. Users of MPW can also build standalone code
- resources, but with less ease.
-
- BBEdit code externals are built as resources of type 'BBXT'. There
- may be any number of code externals per file, and code externals
- can use their own resources. Also, BBXT resources can be of any
- resource ID, since BBEdit manages the externals in such fashion
- that resource name or ID conflicts don't happen. Each BBXT resource
- in a file should have a name as assigned by ResEdit's "Get Info"
- command; this name will appear under the "Extensions" menu in BBEdit.
- (Note that if there are BBXT resources from different files with
- the same name, users may become confused. Neither I nor BBEdit
- will arbitrate extension names.)
-
- BBEdit extensions should be as friendly as possible. They should take
- great care to release any memory that they allocate while running, and
- they should leave no windows on the screen after they return to BBEdit.
- In general, BBEdit extensions should be considered one-shot text filters:
- they do their thing, then exit. They should put no menus in the menu
- bar, and should not have an event loop. (They *can* call ModalDialog. It's
- recommended that you use, or layer on top of, the standard filter that
- BBEdit provides.)
-
- You should assume that any callback will move memory. This means that
- if you keep pointers into any relocatable blocks, pass addresses inside
- relocatable blocks as function arguments, you should lock the block
- first. For maximum friendliness, move it high with MoveHHi() first.
-
- Extensions can put up modal dialogs and alerts, provided they're taken
- down again before the extension exits; they can also call Standard File
- or any system services necessary, as long as no attempt is made to
- bring another application to the front.
-
- All other caveats with respect to managing A4 for code resources with
- globals remain in effect.
-
- PROGRAMMING INTERFACE
-
- Given all of these constraints, what *can* externals do?
-
- The answer is: pretty much any transformation on a window's text that
- they please.
-
- The interface to BBEdit is kept in a structure known as an
- "ExternalCallbackBlock". This structure begins with a 16-bit integer
- which is the version number of a callback block. If the callback
- block passed to you is higher than one you know about, then there
- is additional functionality available that you probably don't know
- about. Conversely, if the version number is less than the one you
- know about, some functionality that your extension requires may not
- be available.
-
- The current callback interface version is 2.
-
- Here is the C structure definition for an ExternalCallbackBlock:
-
- typedef struct {
- short version;
-
- // version 1 callbacks
-
- pascal Handle (*GetWindowContents)(WindowPtr w);
- pascal void (*GetSelection)(long *selStart, long *selEnd, long *firstChar);
- pascal void (*SetSelection)(long selStart, long selEnd, long firstChar);
- pascal void (*GetDocInfo)(WindowPtr w, Str255 fName, short *vRefNum, long *dirID);
- pascal long (*GetModDate)(WindowPtr w);
- pascal Handle (*Copy)(void);
- pascal Handle (*Paste)(Handle pasteText);
-
- // version 2 callbacks
-
- /* Text-Editing stuff */
- pascal long (*GetLastLine)(void);
- pascal long (*GetLineNumber)(long selection);
- pascal long (*GetLineStart)(long selection);
- pascal long (*GetLineEnd)(long selection);
- pascal long (*GetLinePos)(long line);
-
- pascal void (*Insert)(char *text, long len);
- pascal void (*Delete)(void);
-
- /* Getting and Setting window text */
- pascal void (*SetWindowContents)(WindowPtr w, Handle h);
- pascal void (*ContentsChanged)(WindowPtr w);
-
- /* Reading file text */
- pascal Handle (*GetFileText)(short vRefNum, long dirID, Str255 fName, Boolean *canDispose);
-
- /* Direct user-interface calls */
- pascal Boolean (*GetFolder)(Str255 prompt, short *vRefNum, long *dirID);
- pascal Boolean (*OpenSeveral)(Boolean sort, short *file_count, StandardFileReply ***files);
-
- pascal DialogPtr (*CenterDialog)(short dialogID);
- pascal Boolean (*StandardFilter)(DialogPtr d, EventRecord *event, short *item);
- pascal void (*FrameDialogItem)(DialogPtr d, short item);
-
- pascal WindowPtr (*NewDocument)(void);
- pascal WindowPtr (*OpenDocument)(void);
-
- /* Utility Routines */
- pascal Handle (*Allocate)(long size, Boolean clear);
- pascal long (*FindPattern)(char *text, long text_len, long text_offset,
- char *pat, long pat_len,
- Boolean case_sensitive);
-
- pascal void (*ReportOSError)(short code);
-
- /* Preference routines */
- pascal void (*GetPreference)(ResType prefType, short req_len, void *buffer, short *act_len);
- pascal void (*SetPreference)(ResType prefType, short req_len, void *buffer, short *act_len);
- } ExternalCallbackBlock;
-
- Each field of the callback block is a pointer to a routine. Each routine
- is called with the Pascal calling convention; in the following descriptions
- the 'pascal' keyword is omitted for clarity.
-
- Handle (*GetWindowContents)(WindowPtr w);
-
- returns a handle to the text in the window pointed to by "w". This
- routine should only be called on windows which have a windowKind
- of "userKind".
-
- void (*GetSelection)(long *selStart, long *selEnd, long *firstChar);
-
- Sets the 32-bit integers pointed to by the arguments to the character
- offsets of the start of the selection, the end of the selection, and
- the first visible character in the active editing window.
-
- void (*SetSelection)(long selStart, long selEnd, long firstChar);
-
- Sets the selection range and first visible character in the active
- editing window to the values passed. If "firstChar" is -1, the
- selection range will be centered in the window.
-
- void (*GetDocInfo)(WindowPtr w, Str255 *fName, short *vRefNum, short *dirID);
-
- Returns information about the window pointed to by "w". If the window
- corresponds to a document that doesn't exist on disk, then fName will
- be an empty string, and "vRefNum" and "dirID" will be set to zero.
- This routine should only be called on windows with a windowKind of
- "userKind".
-
- long (*GetModDate)(WindowPtr w);
-
- Returns the modification date (in Macintosh time) of the document
- whose window is pointed to by "w". If the document is saved on disk,
- then the last-modified time of the file is returned; otherwise the
- time of last edit is returned.
- This routine should only be called on windows with a windowKind of
- "userKind".
-
- Handle (*Copy)(void);
-
- Returns a handle to a copy of the text enclosed by the current
- selection in the active document. The CALLER is responsible
- for disposing of this handle when finished with it.
-
- Handle (*Paste)(Handle pasteText);
-
- Pastes the text in the handle pointed to by "pasteText" into the
- current selection range of the active document. The CALLER is
- responsible for disposing of this handle when finished with it.
-
- long (*GetLastLine)(void);
-
- Returns the number of lines in the active editing document.
-
- long (*GetLineNumber)(long selection);
-
- Returns the line number of the character offset indicated by 'selection'.
-
- long (*GetLineStart)(long selection);
-
- Returns the character offset of the beginning of the line that 'selection'
- is on.
-
- long (*GetLineEnd)(long selection);
-
- Returns the character offset of the end of the line that 'selection'
- is on.
-
- long (*GetLinePos)(long line);
-
- Returns the character offset of the beginning of 'line'.
-
- void (*Insert)(char *text, long len);
-
- Inserts the 'len' characters pointed to by 'text' in the current selection
- range of the active editing document.
-
- void (*Delete)(void);
-
- Deletes the characters enclosed by the selection range in the active
- editing document.
-
- void (*SetWindowContents)(WindowPtr w, Handle h);
-
- Replaces the contents of the document designated by 'w' with the
- contents of the handle 'h'. •NOTE : after calling SetWindowContents,
- the handle belongs to the window, and MUST NOT BE DISPOSED. Also,
- if you modify the contents or size of the handle pointed to by "h"
- after using it in a SetWindowContents() call, be sure to call
- ContentsChanged() for "w".
-
- void (*ContentsChanged)(WindowPtr w);
-
- This routine should be called if you directly modify the text
- returned from a GetWindowContents() call.
-
- Handle (*GetFileText)(short vRefNum, long dirID, Str255 fName, Boolean *canDispose);
-
- Loads the contents of the designated file's data fork into memory,
- and returns a handle to those contents. If there was an error
- (insufficient memory, file system error, etc), GetFileText()
- will return NIL.
-
- The "canDispose" argument will be set to TRUE if the text was
- loaded from disk, FALSE if the text belongs to an open window.
- In the event that "canDispose" is TRUE, then you should dispose
- of the text (or use it in a SetWindowContents() call). If
- "canDispose" is FALSE, then you MUST NOT DISPOSE THE HANDLE,
- or else you'll crash BBEdit. Also, you must not modify the
- contents of the handle if "canDispose" is FALSE.
-
- Boolean (*GetFolder)(Str255 prompt, short *vRefNum, long *dirID);
-
- Displays a Standard File dialog box for choosing a folder. Returns
- TRUE if a folder was selected, FALSE if the user clicked the
- "Cancel" button. The vRefNum and dirID of the chosen folder are
- returned in "vRefNum", and "dirID", respectively.
-
- Boolean (*OpenSeveral)(Boolean sort, short *file_count, StandardFileReply ***files);
-
- Displays a Standard File box for choosing multiple files at once.
- Returns TRUE if the user chose any files, FALSE if the Cancel
- button was clicked. If "sort" is TRUE, then the files returned
- will be sorted in alphabetical order; otherwise, the files will
- be returned in the order the user added them to the list.
-
- The number of files chosen will be returned in "file_count",
- and a handle to a list of StandardFileReply records (system 7
- style) will be returned in "files".
-
- DialogPtr (*CenterDialog)(short dialogID);
-
- Loads the dialog box indicated by "dialogID" and centers it on the
- screen. The dialog ID should correspond to a dialog which is
- available in the external's resource file, and nowhere else.
-
- Boolean (*StandardFilter)(DialogPtr d, EventRecord *event, short *item);
-
- This standard filter performs some useful standard behavior, such
- as outlining the default button with a thick border, and handling
- activates and deactivates for BBEdit's own windows. It is strongly
- recommended that you pass this pointer as the "filterProc" argument
- when calling ModalDialog() or Alert(). If you're writing custom
- dialog filters in your external, you should call this routine
- directly after doing your own preprocessing.
-
- void (*FrameDialogItem(DialogPtr d, short item);
-
- This routine will draw a rectangle around the dialog item specified.
- If the item is a line, a line will be drawn using "true gray".
-
- WindowPtr (*NewDocument)(void);
-
- Opens a new untitled document, and returns a pointer to its window.
- This document becomes the current document. Will return NIL if for
- some reason the window couldn't be opened.
-
- WindowPtr (*OpenDocument)(void);
-
- Puts up BBEdit's standard Open dialog for choosing a file. If the
- user confirms the dialog and the document is successfully opened,
- returns a pointer to its window. Will return NIL if the user
- cancels the dialog or if an error occurred while opening.
- (If some system error occurs, BBEdit will pose the alert box.)
-
- Handle (*Allocate)(long size, Boolean clear);
-
- Allocates and returns a handle of "size" bytes. If the "clear"
- argument is TRUE, the handle will be zeroed. The handle returned
- will be a real handle, but may reside in MultiFinder temp memory.
- As with any handle, you should avoid locking handles returned by
- Allocate() for any length of time, and you should dispose of the
- handle before returning.
-
- long (*FindPattern)(char *text, long text_len, long text_offset,
- char *pat, long pat_len,
- Boolean case_sensitive);
-
- Searches the text buffer pointed to by "text" for the string of
- characters pointed to by "pat". "text_len" is the amount of text
- to search. "text_offset" is the position relative to the start
- of the text to start searching. "pat_len" is the length of the
- string to match. If "case_sensitive" is TRUE, then the case of
- potential matches will be checked.
-
- FindPattern() will return the offset relative to the start of the
- text that the string was found. If the string was not found,
- FindPattern() will return -1.
-
- void (*ReportOSError)(short code);
-
- Displays an alert box with the proper OS error message corresponding
- to the OS result code given in "code". This is handy for reporting
- filesystem errors, out of memory, and things of that sort.
-
- void (*GetPreference)(ResType prefType, short req_len, void *buffer, short *act_len);
- void (*SetPreference)(ResType prefType, short req_len, void *buffer, short *act_len);
-
- The GetPreference and SetPreference calls are for extensions to use
- to save and retrieve extension-specific information across runs.
- The settings are stored in the BBEdit Prefs file as resources.
-
- GetPreference will retrieve the preference data stored in the
- resource of 'prefType', resource ID 128, and copy the contents
- of that resource into the data pointed to by "buffer". In all
- cases, "req_len" represents the maximum number of bytes which
- will be copied. (WARNING: the amount of data allocated in
- "buffer", be it a static structure or a handle, must be equal
- to or greater than "req_len", or else havoc will occur.)
- The word pointed to by "act_len" will be filled in with the
- actual number of bytes copied; this is always less than
- or equal to "req_len". If "act_len" is negative, the value
- in act_len is an OS error code (usually resNotFound if you're
- calling "GetPreference" with a virgin Preferences file).
-
- SetPreference is the complement of GetPreference; it writes
- out the data in "buffer" to a resource of type "resType".
- "req_len" and "act_len" behave as for GetPreference.
-
- The file containing 'main' for a BBEdit external looks something like this:
-
- #include <SetupA4.h>
- #include "ExternalInterface.h"
-
- pascal void main(ExternalCallbackBlock callbacks, WindowPtr w)
- {
- RememberA0();
- SetupA4();
-
- // if the window is NIL, then don't do anything
-
- if (! w)
- return;
-
- // set the selection range to the start of the document, scrolled to the top
- callbacks.SetSelection(0, 0, 0);
-
- // insert some text at the beginning of the document
- callbacks.Insert("hello world\r", 12);
-
- RestoreA4();
- }
-
- A slightly more elaborate example is given in the "Prefix Lines" external.
-
- OTHER DETAILS
-
- Some things to take care of when building externals:
-
- When BBEdit starts up, it takes account of the externals in the
- "BBEdit Extensions" folder. The "BBEdit Extensions" folder can
- reside in the same folder as BBEdit itself. Under System 6,
- the BBEdit Extensions folder can also reside in the system folder
- on the startup disk; under System 7, the BBEdit Extensions
- folder can also reside in the Extensions folder in the system
- folder on the startup disk.
-
- Files containing externals must be of type 'BBXT'. The creator
- can be anything you like, although files with a creator of
- 'R*ch' will have an icon. (You can, of course, create bundle
- resources and icons to give the files any icons you desire.)
-
- If there are no extensions available, no "Extensions" menu
- will be in the menu bar.
-